const fs = require('fs')
const path = require('path')
const archiver = require('archiver')
const { app: electronApp, shell } = require('electron');
const { spawn } = require('child_process')
const SoundPack = require('soundspack-core').default
const {
    getDataDir,
} = require('ee-core/ps');

/**
 * 是否初始化
 * @returns {boolean} 是否初始化
 */

const isInit = () => {
    const data = getDataDir()
    const cache = path.join(data, 'cache')
    const soundspack = path.join(data, 'soundspack')
    const init = path.join(data, 'config.json')
    if (!fs.existsSync(init) || !fs.existsSync(soundspack)) {
        fs.mkdirSync(cache)
        fs.mkdirSync(soundspack)
        fs.writeFileSync(init, JSON.stringify({
            init: true
        }))
    } else {
        return true
    }
}

/**
 * 获取ffmpeg路径
 * @returns {string} ffmpeg路径
 */
const getffmpeg = () => {
    const data = getDataDir()
    const ffmpeg = path.join(data, 'ffmpeg.exe')
    return ffmpeg
}

/**
 * 判断ffmpeg是否存在
 * @returns {boolean} ffmpeg是否存在
 */
const isffmpeg = () => {
    const data = getDataDir()
    const ffmpeg = path.join(data, 'ffmpeg.exe')
    return fs.existsSync(ffmpeg)
}

/**
 * 获取缓存路径
 * @returns {string} 缓存路径
 */
const getCache = () => {
    const data = getDataDir()
    const cache = path.join(data, 'cache')
    return cache
}

/**
 * 获取音频包导出路径
 * @returns {string} 音频包导出路径
 */
const getSoundspack = () => {
    const data = getDataDir()
    const soundspack = path.join(data, 'soundspack')
    return soundspack
}

/**
 * 判断音频包缓存是否存在
 * @param {string} name 音乐包名称
 * @returns {boolean} 音频包缓存是否存在
 */

const isSoundsPackCache = (name) => {
    const Cache = getCache();
    const soundspack_name = path.join(Cache, name);
    return fs.existsSync(soundspack_name)
}

/**
 * 判断音频包是否存在
 * @param {string} name 音乐包名称
 * @returns {string} 音频包导出路径
 */

const isSoundsPack = (name) => {
    const soundspack = getSoundspack();
    const soundspack_name = path.join(soundspack, `${name}.zip`);
    return fs.existsSync(soundspack_name)
}

/**
 * 创建音频包
 * @param {object} value 参数
 * @returns {object} 音频包路径
 */
const createSoundsPackCache = (value) => {
    if (!value && !value.name) {
        return new Error('参数为空')
    }
    const Cache = getCache();
    const soundspack = path.join(Cache, value.name);
    const folderStructure = path.join(soundspack, "assets", "minecraft", "sounds")
    const mcmeta = path.join(soundspack, 'pack.mcmeta')
    const sounds = path.join(soundspack, "assets", "minecraft", 'sounds.json')

    // 如果目录已存在，先删除它
    if (fs.existsSync(soundspack)) {
        fs.rmSync(soundspack, { recursive: true, force: true });
    }

    // 创建所需的文件夹结构
    fs.mkdirSync(folderStructure, { recursive: true });
    // 写入pack.mcmeta文件
    fs.writeFileSync(mcmeta, JSON.stringify({
        pack: {
            description: value.description ? value.description + "\n§b§lPowered by Minecraft Soundspack" : "§b§lPowered by Minecraft Soundspack",
            pack_format: Number(value.version ?? 1)
        }
    }, null, 2), { encoding: 'utf8', flag: 'w' })

    // 写入空的sounds.json文件
    fs.writeFileSync(sounds, JSON.stringify({
    }, null, 2), { encoding: 'utf8', flag: 'w' })

    return {
        root: soundspack.replace(/\\/g, '/'),
        sounds: folderStructure.replace(/\\/g, '/'),
        mcmeta: mcmeta.replace(/\\/g, '/'),
        sounds_json: sounds.replace(/\\/g, '/')
    }
}

/**
 * 完善音乐包
 * @param {string} name 音乐包名称
 * @returns {Promise<object>} status是否完善成功, keyValue: 音频名
 */
const createSoundsPack = async (name) => {
    if (!name) {
        throw new Error('音乐包名称不能为空')
    }

    try {
        const soundspack_cfg = new SoundPack(name);
        const soundspack_src = path.join(getCache(), name);
        const folderStructure = path.join(soundspack_src, "assets", "minecraft", "sounds")
        const soundsjson = path.join(soundspack_src, "assets", "minecraft", 'sounds.json')

        // 检查必要的目录是否存在
        if (!fs.existsSync(folderStructure)) {
            throw new Error('音频文件目录不存在')
        }

        // 读取sounds文件夹中的所有ogg文件
        const soundFiles = fs.readdirSync(folderStructure)
            .filter(file => file.endsWith('.ogg'))

        if (soundFiles.length === 0) {
            throw new Error('没有找到任何ogg音频文件')
        }

        // 为每个ogg文件创建对应的sounds_json配置
        const sounds_json = soundFiles.map(file => {
            const fileName = file.replace('.ogg', '')
            return {
                keyValue: fileName,
                category: 'record',
                sounds: soundspack_cfg.setSound([{
                    name: fileName
                }])
            }
        })

        const res_json = soundspack_cfg.createSoundsConfig(sounds_json)

        const soundNames = Object.keys(res_json);

        // 写入sounds.json文件
        await fs.promises.writeFile(
            soundsjson,
            JSON.stringify(res_json, null, 2),
            { encoding: 'utf8', flag: 'w' }
        )

        return {
            status: true,
            keyValue: soundNames
        }
    } catch (error) {
        console.error('完善音乐包时发生错误:', error)
        throw error
    }
}

/**
 * 生成Minecraft播放音乐的命令
 * @param {string} value - 音乐的标识符
 * @returns {string} 返回完整的播放命令
 */
const toCommand = (value) =>{
    return `/playsound ${value} record @a ~ ~ ~ 10000`
}

/**
 * 将指定名称的音乐包打包成 zip
 * @param {string} name 音乐包名称
 * @returns {Promise<string>} 打包完成后的 zip 文件路径
 */
const toSoundsPackZip = async (name) => {
    if (!name) {
        throw new Error('音乐包名称不能为空')
    }

    try {
        
        const isCreate = await createSoundsPack(name)
        if (!isCreate) {
            throw new Error('音乐包完善失败')
        }

        const sourcePath = path.join(getCache(), name)
        const targetPath = path.join(getSoundspack(), `${name}.zip`)

        // 检查源文件夹是否存在
        if (!fs.existsSync(sourcePath)) {
            throw new Error('源文件夹不存在')
        }

        return new Promise((resolve, reject) => {
            const output = fs.createWriteStream(targetPath)
            const archive = archiver('zip', {
                zlib: { level: 9 } // 最高压缩级别
            })

            // 错误处理
            output.on('error', (err) => {
                reject(new Error(`创建输出流失败: ${err.message}`))
            })

            archive.on('error', (err) => {
                reject(new Error(`压缩过程发生错误: ${err.message}`))
            })

            // 完成处理
            output.on('close', () => {
                resolve({
                    path: targetPath,
                    cmd: isCreate
                })
            })

            archive.pipe(output)

            // 将整个文件夹添加到压缩包
            archive.directory(sourcePath, false)

            // 完成打包
            archive.finalize()
        })
    } catch (error) {
        throw new Error(`打包音乐包失败: ${error.message}`)
    }
}

/**
 * 将指定的音频文件转换成 ogg 格式
 * @param {string} sourcePath - 待转换的音频文件路径
 * @param {string} value - 转换后音乐文件的名称
 * @param {string} name - 音乐包名称
 * @returns {Promise<string>} 转换完成后的 ogg 文件路径
 */
const convertToOgg = async (name, sourcePath, value) => {
    return new Promise((resolve, reject) => {
        // 检查源文件是否存在
        if (!fs.existsSync(sourcePath)) {
            reject(new Error('源文件不存在'));
            return;
        }

        const soundspack = path.join(getCache(), name);
        const folderStructure = path.join(soundspack, "assets", "minecraft", "sounds")

        // 确保目标目录存在
        if (!fs.existsSync(folderStructure)) {
            fs.mkdirSync(folderStructure, { recursive: true });
        }

        // 构建目标文件路径
        const targetPath = path.join(folderStructure, `${value}.ogg`);
        const ffmpegPath = getffmpeg();

        // 检查ffmpeg是否存在
        if (!isffmpeg()) {
            reject(new Error('FFmpeg 未安装或路径不正确'));
            return;
        }

        // 构建ffmpeg命令行参数
        const args = [
            '-i', sourcePath,
            '-c:a', 'libvorbis',  // 使用 libvorbis 编码器
            '-y',                 // 覆盖已存在的文件
            targetPath
        ];

        // 使用spawn执行ffmpeg命令
        const ffmpegProcess = spawn(ffmpegPath, args);

        // 收集错误输出
        let errorOutput = '';
        ffmpegProcess.stderr.on('data', (data) => {
            errorOutput += data.toString();
        });

        // 处理命令执行结果
        ffmpegProcess.on('close', (code) => {
            if (code === 0) {
                // 检查输出文件是否成功创建
                if (fs.existsSync(targetPath)) {
                    resolve(targetPath);
                } else {
                    reject(new Error('转换成功但输出文件未找到'));
                }
            } else {
                reject(new Error(`FFmpeg 转换失败，错误码: ${code}\n${errorOutput}`));
            }
        });

        ffmpegProcess.on('error', (err) => {
            reject(new Error(`FFmpeg 进程启动失败: ${err.message}`));
        });

        // 设置超时处理
        const timeout = setTimeout(() => {
            ffmpegProcess.kill();
            reject(new Error('FFmpeg 转换超时'));
        }, 300000); // 5分钟超时

        // 清理超时计时器
        ffmpegProcess.on('close', () => {
            clearTimeout(timeout);
        });
    });
};

/**
 * 将指定的文件复制到指定的文件夹内
 * @param {string} sourcePath - 待复制的文件路径
 * @param {string} targetDir - 目标文件夹路径
 * @returns {Promise<void>} 复制完成的 Promise
 */
const copyFileToFolder = (sourcePath, targetDir) => {
    return new Promise((resolve, reject) => {
        // 读取待复制的文件内容
        fs.readFile(sourcePath, (err, data) => {
            if (err) {
                reject(err)
                return
            }
            // 构建目标文件路径
            const targetPath = path.join(targetDir, path.basename(sourcePath))
            // 将文件内容写入目标文件
            fs.writeFile(targetPath, data, (err) => {
                if (err) {
                    reject(err)
                    return
                }
                resolve()
            })
        })
    })
}


/**
 * 将指定的文件夹打包成 zip 文件并保存到指定的文件夹内
 * @param {string} sourceDir - 待打包的文件夹路径
 * @param {string} targetDir - 目标文件夹路径
 * @param {string} zipFileName - zip 文件名
 * @returns {Promise<string>} 打包完成后的 zip 文件路径
 */
const packFolderToZip = (sourceDir, targetDir, zipFileName) => {
    return new Promise((resolve, reject) => {
        // 创建可写流，并指定压缩文件的存储位置和文件名
        const output = fs.createWriteStream(path.join(targetDir, `${zipFileName}.zip`))
        // 创建 archiver 实例，指定要使用的压缩算法
        const archive = archiver('zip', {
            zlib: { level: 9 }
        })

        // 打包过程中发生错误的处理
        output.on('error', (err) => {
            reject(err)
        })

        // 打包过程结束的处理
        output.on('close', () => {
            resolve(output.path)
        })

        // 将可写流与 archiver 绑定，用于将压缩数据输出到可写流中
        archive.pipe(output)

        // 读取待压缩文件夹下的所有文件，并添加到压缩包中
        fs.readdirSync(sourceDir).forEach((file) => {
            const filePath = path.join(sourceDir, file)
            archive.file(filePath, { name: file })
        })

        // 完成所有文件的添加，结束压缩过程
        archive.finalize()
    })
}

// 处理文件名
const processFileName = (fileName) => {
    // 移除扩展名
    const nameWithoutExt = fileName.substring(0, fileName.lastIndexOf('.'));

    // 判断是否为中文
    const isChinese = /[\u4e00-\u9fa5]/.test(nameWithoutExt);

    if (isChinese) {
        // 中文名称转拼音并取首字母
        let result = pinyin(nameWithoutExt, { toneType: 'none', type: 'array' })
            .map(word => word.charAt(0))
            .join('')
            .toLowerCase()
            .replace(/[^a-z0-9]/g, ''); // 移除非字母数字字符

        // 限制长度为5个字符
        if (result.length > 5) {
            result = result.substring(0, 5);
        }

        return result;
    } else {
        // 英文名称缩写处理
        let result = nameWithoutExt
            .replace(/[^a-zA-Z0-9]/g, '') // 移除非字母数字字符
            .toLowerCase();

        // 如果长度超过5个字符，进行缩写
        if (result.length > 5) {
            // 提取首字母和重要字符
            const firstChar = result.charAt(0);
            const lastChar = result.charAt(result.length - 1);

            if (result.length <= 3) {
                // 如果长度小于等于3，直接返回
                return result;
            } else if (result.length <= 5) {
                // 如果长度小于等于5，直接返回
                return result;
            } else {
                // 取首字母、中间字符和尾字母，再加上两个重要位置的字符
                const pos1 = Math.floor(result.length / 3);
                const pos2 = Math.floor(result.length * 2 / 3);
                result = firstChar + result.charAt(pos1) + result.charAt(pos2) + lastChar;

                // 如果还是超过5个字符，截取前5个
                if (result.length > 5) {
                    result = result.substring(0, 5);
                }
            }
        }

        return result;
    }
};

// 打开文件夹
const openExeFolder = () => {
    shell.openPath(getSoundspack());
};

module.exports = {
    isInit,
    getCache,
    getSoundspack,
    isSoundsPackCache,
    createSoundsPackCache,
    isSoundsPack,
    toSoundsPackZip,
    packFolderToZip,
    convertToOgg,
    getffmpeg,
    isffmpeg,
    copyFileToFolder,
    processFileName,
    openExeFolder
};